/*
 * @Author       : Bing Chen chenbing@iocharger.com
 * @Date         : 2025-01-21 16:09:28
 * @LastEditors  : Bing Chen chenbing@iocharger.com
 * @LastEditTime : 2025-03-15 13:06:34
 * @FilePath     : \openchaoji-corelib\x86\app_hal_x86.c
 * @Description  :
 *
 * Copyright (c) 2025 OpenChaoJi
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the icense for the specific language governing permissions and
 * limitations under the License.
 *
 */
#include "app_hal_x86.h"

/**
 * 模拟CAN报文接收缓冲队列
 */
// Initialize the FIFO queue
void fifo_init(fifo_queue *q) {
    q->front = 0;
    q->rear = 0;
    q->count = 0;
}

// Check if the queue is full
int fifo_is_full(fifo_queue *q) {
    return q->count == HAL_UDP_PACKET_QUEUE_SIZE;
}

// Check if the queue is empty
int fifo_is_empty(fifo_queue *q) {
    return q->count == 0;
}

// Enqueue an element into the queue
int fifo_enqueue(fifo_queue *q, struct can_pdu *pdu) {
    if (fifo_is_full(q)) {
        fprintf(stderr, "Queue is full\n");
        return -1;
    }
    q->queue[q->rear] = *pdu;
    q->rear = (q->rear + 1) % HAL_UDP_PACKET_QUEUE_SIZE;
    q->count++;
    return 0;
}

// Dequeue an element from the queue
int fifo_dequeue(fifo_queue *q, struct can_pdu *pdu) {
    if (fifo_is_empty(q)) {
        // fprintf(stderr, "Queue is empty\n");
        return -1;
    }
    *pdu = q->queue[q->front];
    q->front = (q->front + 1) % HAL_UDP_PACKET_QUEUE_SIZE;
    q->count--;
    return 0;
}


/**
 * 模拟CAN的UDP Socket
 */
int hal_create_udp_socket(uint16_t port) {
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        perror("Failed to create socket");
        return -1;
    }

    // Enable SO_REUSEPORT
    int opt = 1;
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)) < 0) {
        perror("Failed to set SO_REUSEPORT");
        close(sockfd);
        return -1;
    }

    // Set timeout to 1ms
    struct timeval timeout;
    timeout.tv_sec = 0;
    timeout.tv_usec = 1000; // 1ms
    if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) {
        perror("Failed to set timeout");
        close(sockfd);
        return -1;
    }

    // Bind the socket to port 27930
    struct sockaddr_in addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(port);

    if (bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        perror("Failed to bind socket");
        close(sockfd);
        return -1;
    }
    LOG_DATA_HAL("socket bind to %d", port);

    return sockfd;
}

void hal_init_dest_socket_addr(const char * addr, uint16_t port, struct sockaddr_in *dest_addr)
{
    dest_addr->sin_family = AF_INET;
    dest_addr->sin_port = htons(port);
    inet_pton(AF_INET, addr, &dest_addr->sin_addr);
}

/**
 * 每次检查是否有UDP(CAN)报文, 如有则填入缓冲队列, 模拟底层异步读取CAN包
 *
 * 如果遇到用于同步开关状态的特殊can id, 则调用同步函数, 传入同步数据
 */
int hal_try_read_can_pdu(OpenChaoJi_hal_handle handle, int socket_fd, fifo_queue * recv_queue, can_data_pilot_sync_func pilot_sync_func, struct can_pdu * can_recv_info)
{
    size_t len = HAL_UDP_PACKET_LEN * HAL_UDP_PACKET_QUEUE_SIZE;
    unsigned char buff[HAL_UDP_PACKET_LEN * HAL_UDP_PACKET_QUEUE_SIZE];

    struct sockaddr_in addr;
    int addr_len=sizeof(struct sockaddr_in);
    int left = recvfrom(socket_fd, buff, len, 0, (struct sockaddr*)&addr,(socklen_t*)&addr_len);
    if (left > 0)
    {
        // LOG_DATA_HAL("recv from %d", ntohs(addr.sin_port));

        unsigned char * p = buff;

        while (left >= 6)        // 最少id+dlc+1个字节
        {
            struct can_pdu pdu;
            memset(&pdu, 0x00, sizeof(pdu));

            pdu.can_id.uint32_tWord = *((uint32_t *)(p + 0));   p += 4;
            pdu.can_dlc = *p;                                   p += 1;
            if (left < (5 + pdu.can_dlc))
            {
                break;
            }

            memcpy(pdu.data, p, pdu.can_dlc);               p += pdu.can_dlc;

            if (pdu.can_id.uint32_tWord == CAN_ID_PILOT_SYNC)
            {
                (*pilot_sync_func)(handle, &pdu);
            }
            else
            {
                fifo_enqueue(recv_queue, &pdu);
            }

            left -= (5 + pdu.can_dlc);
        }
    }

    // 一次从队列中只能取一个CAN包
    int rtn = fifo_dequeue(recv_queue, can_recv_info);
    if (rtn == 0)
    {
        LOG_DATA_HAL("<-[%08X][%d]", can_recv_info->can_id.uint32_tWord, can_recv_info->can_dlc);
        LOG_DATA_BUFF_HAL(can_recv_info->data, can_recv_info->can_dlc);
    }

    return rtn;
}

int hal_send_can_pdu(OpenChaoJi_hal_handle handle, int socket_fd, struct sockaddr_in *dest_addr, const struct can_pdu * can_send_info)
{
    if (can_send_info->can_dlc > 8)
    {
        LOG_ERR("CAN DLC > 8");
        return -1;
    }

    if (can_send_info->can_id.uint32_tWord != CAN_ID_PILOT_SYNC)
    {
        LOG_DATA_HAL("->[%08X][%d]", can_send_info->can_id.uint32_tWord, can_send_info->can_dlc);
        LOG_DATA_BUFF_HAL(can_send_info->data, can_send_info->can_dlc);
    }

    unsigned char packet[HAL_UDP_PACKET_LEN];
    *(uint32_t *)(packet + 0) = can_send_info->can_id.uint32_tWord;
    packet[4] = can_send_info->can_dlc;
    memcpy(packet + 5, can_send_info->data, can_send_info->can_dlc);

    int addr_len=sizeof(struct sockaddr_in);

    // LOG_DATA_HAL("send to %d", ntohs(dest_addr->sin_port));
    int rtn = sendto(socket_fd, packet, (size_t)(can_send_info->can_dlc + 5), 0, (struct sockaddr*)dest_addr, addr_len);
    if (rtn <= 0)
    {
        LOG_CRIT("Send CAN FAILED");
        return -1;
    }

    return 0;
}

void hal_log_pilot_sync(struct can_data_pilot_sync *sync)
{
    char tmp[256];

    sprintf(tmp,
    "pilot_type=%d \n"
    "evcc:plug=%d pe_lost=%d S=%d S2=%d S2p=%d Sv=%d dc=%d lock=%d emgy=%d vol=%d\n"
    "secc:pe_lost=%d S0=%d S1=%d dc=%d ac=%d aux=%d lock=%d emgy=%d power=%d"
    , sync->pilot_type
    , sync->evcc_plug, sync->evcc_pe_lost, sync->evcc_S, sync->evcc_S2, sync->evcc_S2p, sync->evcc_Sv, sync->evcc_dc, sync->evcc_lock, sync->evcc_emergency, sync->evcc_battery_voltage
    , sync->secc_pe_lost, sync->secc_S0, sync->secc_S1, sync->secc_dc, sync->secc_ac, sync->secc_aux, sync->secc_lock, sync->secc_emergency, sync->secc_output_power
    );

    LOG_PILOT_HAL("%s", tmp);
}


/**
 * 根据当前的开关状态判断当前系统类型, Utility层使用电压判断, HAL模拟则使用开关组合状态来判断, 用于得到对应的电压给Utility层
 */
enum OpenChaoJi_pilot_status_gbt18487_a hal_pilot_status_gbt18487_a(struct can_data_pilot_sync *sync)
{
    if (sync->pilot_type != HA_SIMU_PILOT_A_A)  // A类系统
    {
        return OPENCHAOJI_PILOT_STATUS_GBT18487_A_UNKNOWN;
    }

    if (sync->secc_pe_lost)
    {
        return OPENCHAOJI_PILOT_STATUS_GBT18487_A_DISCONNECT;
    }

    if (sync->evcc_pe_lost)
    {
        return OPENCHAOJI_PILOT_STATUS_GBT18487_A_DISCONNECT;
    }

    if (sync->evcc_plug)     // 全连接
    {
        return OPENCHAOJI_PILOT_STATUS_GBT18487_A_CONNECTED;  // 4V
    }
    else if (sync->evcc_S) // 半连接
    {
        return OPENCHAOJI_PILOT_STATUS_GBT18487_A_S1_OPEN;  // 6V
    }
    else  // 无连接
    {
        return OPENCHAOJI_PILOT_STATUS_GBT18487_A_DISCONNECT; // 12V
    }
}

#if (OPENCHAOJI_CONFIG_ENABLE_GBT27930_B == CONFIG_YES)

enum OpenChaoJi_pilot_status_gbt18487_b hal_pilot_status_gbt18487_b(struct can_data_pilot_sync *sync)
{
    if (sync->pilot_type != HA_SIMU_PILOT_B_B)  // B类系统
    {
        return OPENCHAOJI_PILOT_STATUS_GBT18487_B_UNKNOWN;
    }

    if (sync->secc_pe_lost)                                    // 状态A 能量 传 输 阶 段 充 电 机 侧 PE断线
    {
        return OPENCHAOJI_PILOT_STATUS_GBT18487_B_A;  // 12V
    }

    if (sync->evcc_pe_lost)                                    // 状态A 能量 传 输 阶 段 车 辆 接  口 PE断 针 或 车 侧 PE 断线
    {
        return OPENCHAOJI_PILOT_STATUS_GBT18487_B_A;  // 12V
    }

    if (!sync->evcc_plug)                                       // 状态A 未插枪
    {
        return OPENCHAOJI_PILOT_STATUS_GBT18487_B_A;  // 12V
    }

    // 以下是完全插合 且 PE未断线各种情况
    if (!sync->secc_S1 && !sync->evcc_S2p && !sync->evcc_S2)        // 状态A' 完全插合（对于S2p常开） S1开 S2p开 S2开, Sv开/合
    {
        return OPENCHAOJI_PILOT_STATUS_GBT18487_B_Ap;
    }

    if (!sync->secc_S1 && sync->evcc_S2p && !sync->evcc_S2)        // 状态B 完全插合后车切换电路（对于S2p常开）、完全插合（对于S2p常闭）、车桩预约中 、拔枪前 S1开 S2p合 S2开, Sv开/合
    {
        return OPENCHAOJI_PILOT_STATUS_GBT18487_B_B;
    }

    if (sync->secc_S1 && sync->evcc_S2p && !sync->evcc_S2)          // 状态C 充电机准备就绪 S1合 S2p合 S2开, Sv开/合
    {
        return OPENCHAOJI_PILOT_STATUS_GBT18487_B_C;
    }

    if (sync->secc_S1 && sync->evcc_S2p && sync->evcc_S2)        // 状态D 能量传输中 S1合 S2p合 S2合, Sv开
    {
        return OPENCHAOJI_PILOT_STATUS_GBT18487_B_D;
    }

    if (!sync->secc_S1 && sync->evcc_S2p && sync->evcc_S2)          // 状态E 车辆准备就绪 S1开 S2p合 S2合, Sv开/合
    {
        return OPENCHAOJI_PILOT_STATUS_GBT18487_B_E;
    }

    return OPENCHAOJI_PILOT_STATUS_GBT18487_B_UNKNOWN;              // 未知状态
}
#endif

// 根据当前的开关状态判断三个检测点电压
// 检测点1, SECC端
float hal_pilot_voltage_secc_checkpoint_1(struct can_data_pilot_sync *sync)
{
    if (sync->pilot_type == HA_SIMU_PILOT_A_A)  // A类系统
    {
        enum OpenChaoJi_pilot_status_gbt18487_a status = hal_pilot_status_gbt18487_a(sync);
        switch (status)
        {
        case OPENCHAOJI_PILOT_STATUS_GBT18487_A_DISCONNECT: return 12.0;
        case OPENCHAOJI_PILOT_STATUS_GBT18487_A_S1_OPEN:    return 6.0;
        case OPENCHAOJI_PILOT_STATUS_GBT18487_A_CONNECTED:  return 4.0;
        default:                                            return INVALID_VOLTAGE; // 故障电压
        }
    }
#if (OPENCHAOJI_CONFIG_ENABLE_GBT27930_B == CONFIG_YES)
    else if (sync->pilot_type == HA_SIMU_PILOT_B_B)  // B类系统
    {
        enum OpenChaoJi_pilot_status_gbt18487_b status = hal_pilot_status_gbt18487_b(sync);
        switch (status)
        {
        case OPENCHAOJI_PILOT_STATUS_GBT18487_B_A:          return 12.0;
        case OPENCHAOJI_PILOT_STATUS_GBT18487_B_Ap:         return 10.8;
        case OPENCHAOJI_PILOT_STATUS_GBT18487_B_B:          return 2.34;
        case OPENCHAOJI_PILOT_STATUS_GBT18487_B_C:          return 8.73;
        case OPENCHAOJI_PILOT_STATUS_GBT18487_B_D:          return 5.6;
        case OPENCHAOJI_PILOT_STATUS_GBT18487_B_E:          return 0.88;
        default:                                            return INVALID_VOLTAGE; // 故障电压
        }
    }
#endif
    return INVALID_VOLTAGE; // 故障电压
}

float hal_pilot_voltage_evcc_checkpoint_2(struct can_data_pilot_sync *sync)
{
    if (sync->pilot_type == HA_SIMU_PILOT_A_A)  // A类系统
    {
        enum OpenChaoJi_pilot_status_gbt18487_a status = hal_pilot_status_gbt18487_a(sync);
        switch (status)
        {
        case OPENCHAOJI_PILOT_STATUS_GBT18487_A_DISCONNECT: return 12.0;
        case OPENCHAOJI_PILOT_STATUS_GBT18487_A_S1_OPEN:    return 6.0;
        case OPENCHAOJI_PILOT_STATUS_GBT18487_A_CONNECTED:  return 6.0;     // 车端全连接和半连接都是6V
        default:                                            return INVALID_VOLTAGE; // 故障电压
        }
    }
#if (OPENCHAOJI_CONFIG_ENABLE_GBT27930_B == CONFIG_YES)
    else if (sync->pilot_type == HA_SIMU_PILOT_B_B)  // B类系统
    {
        enum OpenChaoJi_pilot_status_gbt18487_b status = hal_pilot_status_gbt18487_b(sync);
        switch (status)
        {
        case OPENCHAOJI_PILOT_STATUS_GBT18487_B_A:          return 0.0;
        case OPENCHAOJI_PILOT_STATUS_GBT18487_B_Ap:         return 10.8;
        case OPENCHAOJI_PILOT_STATUS_GBT18487_B_B:          return 2.34;
        case OPENCHAOJI_PILOT_STATUS_GBT18487_B_C:          return 8.73;
        case OPENCHAOJI_PILOT_STATUS_GBT18487_B_D:          return 5.6;
        case OPENCHAOJI_PILOT_STATUS_GBT18487_B_E:          return 0.88;
        default:                                            return INVALID_VOLTAGE; // 故障电压
        }
    }
#endif

    return INVALID_VOLTAGE; // 故障电压
}
// 检测点3仅(B类系统)
float hal_pilot_voltage_evcc_checkpoint_3(struct can_data_pilot_sync *sync)
{
    if (sync->pilot_type == HA_SIMU_PILOT_B_B)  // B类系统
    {
        return (sync->evcc_Sv) ? 6.0 : 12.0;
    }
    else if (sync->pilot_type == HA_SIMU_PILOT_B_A)  // B类EVCC兼容A类SECC
    {
        return (sync->evcc_Sv) ? 8.0 : 12.0;
    }

    return INVALID_VOLTAGE; // 故障电压
}

void hal_x86_relay_init(struct hal_x86_relay * relay, const char * name, uint32_t delayms_close, uint32_t delayms_open, uint8_t close)
{
    relay->name = name;
    relay->delay_ms_close = delayms_close;
    relay->delay_ms_open = delayms_open;
    relay->close = close;
    relay->close_ticks = 0;
}


void hal_x86_relay_set(int idx, struct hal_x86_relay * relay, int close)
{
    if (close == 1)
    {
        if (relay->close == 0)
        {
            LOG_PILOT_HAL("[%d]relay %s close", idx, relay->name);
            relay->close = 1;
            relay->close_ticks = OpenChaoJi_os_get_ticks();
        }
    }
    else
    {
        if (relay->close == 1)
        {
            LOG_PILOT_HAL("[%d]relay %s open", idx, relay->name);
            relay->close = 0;
            relay->close_ticks = OpenChaoJi_os_get_ticks();
        }
    }
}

void hal_x86_relay_service(int idx, struct hal_x86_relay * relay)
{
    char file_name[64];

    // 判断闭合
    sprintf(file_name, "/tmp/relay_close_%s", relay->name);
    if (access(file_name, F_OK) == 0)
    {
        remove(file_name);

        hal_x86_relay_set(idx, relay, 1);
    }

    // 判断断开
    sprintf(file_name, "/tmp/relay_open_%s", relay->name);
    if (access(file_name, F_OK) == 0)
    {
        remove(file_name);

        hal_x86_relay_set(idx, relay, 0);
    }
}


uint8_t hal_x86_relay_get(int idx, struct hal_x86_relay * relay)
{
    if (relay->close_ticks == 0)
    {
        return relay->close;
    }

    uint32_t now_ticks = OpenChaoJi_os_get_ticks();

    if (relay->close == 1)
    {
        if (now_ticks - relay->close_ticks >= relay->delay_ms_close)
        {
            return 1;
        }
        return 0;
    }
    else
    {
        if (now_ticks - relay->close_ticks >= relay->delay_ms_open)
        {
            return 0;
        }
        return 1;
    }
}
